package co.fitstart.signin.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.demo2do.core.entity.exception.BusinessException;
import com.demo2do.core.persistence.GenericDaoSupport;
import com.demo2do.core.persistence.annotation.Dao;
import com.demo2do.core.utils.DateFormatUtils;

import co.fitstart.entity.Constant;
import co.fitstart.entity.coach.CoachSigninRecord;
import co.fitstart.entity.coach.CoachSigninStatistic;
import co.fitstart.entity.coach.CoachStudentRelation;
import co.fitstart.entity.course.CourseType;
import co.fitstart.entity.course.CourseUnit;
import co.fitstart.entity.course.round.CourseRound;
import co.fitstart.entity.course.round.CourseRoundResult;
import co.fitstart.entity.course.schedule.CourseSchedule;
import co.fitstart.entity.course.schedule.CourseScheduleAttribute;
import co.fitstart.entity.course.schedule.ScheduleStatus;
import co.fitstart.entity.course.schedule.SigninRecord;
import co.fitstart.entity.course.schedule.StudentSigninRecord;
import co.fitstart.entity.order.Order;
import co.fitstart.entity.order.OrderStatus;
import co.fitstart.entity.principal.Principal;
import co.fitstart.entity.user.UserSource;
import co.fitstart.entity.user.bwh.BwhRecord;
import co.fitstart.entity.user.inbody.InbodyRecord;
import co.fitstart.signin.dao.BwhRecordDao;
import co.fitstart.signin.dao.CourseRoundDao;
import co.fitstart.signin.dao.InbodyRecordDao;
//import co.fitstart.signin.dao.CourseRoundDao;
//import co.fitstart.signin.dao.InbodyRecordDao;
import co.fitstart.signin.dao.SigninDao;
import co.fitstart.signin.dao.StudentTargetDao;
import co.fitstart.signin.dao.UserSourceDao;
import co.fitstart.signin.service.SigninService;

/**
 * 
 * @author dan
 *
 */
@Service("signinService")
public class SigninServiceImpl implements SigninService{
    
    private Logger logger = LoggerFactory.getLogger(SigninServiceImpl.class);
    
    @Dao
    private SigninDao signinDao;
    
    @Dao
    private UserSourceDao userSourceDao;
    
    @Dao
    private BwhRecordDao bwhRecordDao;
    
    @Dao
    private StudentTargetDao studentTargetDao;
    
    @Dao
    private CourseRoundDao courseRoundDao;
    
    @Dao
    private InbodyRecordDao inbodyRecordDao;
    
    @Autowired
    private GenericDaoSupport genericDaoSupport;
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.signin.service.SigninService#list()
     */
    public List<StudentSigninRecord> list() {
      return signinDao.list();
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.signin.service.SigninService#load(java.lang.Long, java.lang.String)
     */
    public StudentSigninRecord load(Long siteId, String message) {
        return signinDao.load(siteId, message);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.signin.service.SigninService#loadCourseSchedule(java.lang.Long)
     */
    public CourseSchedule loadCourseSchedule(Long courseScheduleId) {
        return genericDaoSupport.load(CourseSchedule.class, courseScheduleId);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.signin.service.SigninService#countFinishedSchedule(co.fitstart.entity.course.round.CourseRound)
     */
    public int countFinishedSchedule(CourseRound courseRound) {
        return signinDao.countFinishedSchedule(courseRound, ScheduleStatus.FINISH);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.signin.service.SigninService#showSigninRecord(co.fitstart.entity.course.schedule.StudentSigninRecord)
     */
    public void showSigninRecord(StudentSigninRecord record) {
        genericDaoSupport.update(record.show());
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.signin.service.SigninService#signin(co.fitstart.entity.course.schedule.CourseSchedule, co.fitstart.entity.principal.Principal, co.fitstart.entity.user.bwh.BwhRecord)
     */
    public void signin(CourseSchedule courseSchedule, Principal siteServer, BwhRecord bwhRecord) {
       if(courseSchedule.getCourseType().equals(CourseType.CARD)) {
        	
        	// check whether this is an admin operation and check whether course schedule could be signin
            if(!courseSchedule.couldSignin()) {
                throw new BusinessException("不在签到时限内，请后台手动签到");
            }
            
            // check scheduleStatus of this courseSchedule in order to avoid multiple-time sign in
            if(courseSchedule.getScheduleStatus().isFinish()){
                logger.error("#signin() with params courseScheduleId[{}], coachId[{}], studentId[{}], siteId[{}], 无法重复签到", courseSchedule.getId(), courseSchedule.getCoach().getId(), courseSchedule.getStudent().getId(), courseSchedule.getSite().getId());
                throw new BusinessException("无法重复签到");
            }
            
            
            // 2. sign in for course schedule
            genericDaoSupport.update(courseSchedule.signin(null));
            
            // 3. create course schedule attribute
            this.createCourseScheduleAttr(courseSchedule, siteServer);
            
            CourseRound courseRound = courseSchedule.getCourseRound();
            genericDaoSupport.update(courseRound.signin());
        	
        } else {
        	
        	logger.info("#signin() with params courseScheduleId [{}], coachId[{}], studentId [{}]", courseSchedule.getId(), courseSchedule.getCoach().getId(), courseSchedule.getStudent().getId());
             
        	// check whether this is an admin operation and check whether course schedule could be signin
            if(!courseSchedule.couldSignin()) {
                throw new BusinessException("不在签到时限内，请后台手动签到");
            }
            
            // check scheduleStatus of this courseSchedule in order to avoid multiple-time sign in
            if(courseSchedule.getScheduleStatus().isFinish()){
                logger.error("#signin() with params courseScheduleId[{}], coachId[{}], studentId[{}], siteId[{}], 无法重复签到", courseSchedule.getId(), courseSchedule.getCoach().getId(), courseSchedule.getStudent().getId(), courseSchedule.getSite().getId());
                throw new BusinessException("无法重复签到");
            }
            boolean isBwhNeeded = this.isBwhNeeded(courseSchedule);
            
            // 1. check dishonesty
            CourseRound round = courseSchedule.getCourseRound();
            if(round != null && round.isDishonesty()) {
                List<ScheduleStatus> scheduleStatus = new ArrayList<>();
                scheduleStatus.add(ScheduleStatus.FINISH);
                scheduleStatus.add(ScheduleStatus.EXPIRY);
                
                List<CourseSchedule> schedules = signinDao.listCourseSchedule(round, scheduleStatus, 0, 1);
                if(schedules == null || schedules.size() == 0 
                        || schedules.get(0).getScheduleStatus() == ScheduleStatus.FINISH) {
                    genericDaoSupport.update(round.editAttribute("dishonesty", "0"));
                }
            }
            
            // 2. sign in for course schedule
            genericDaoSupport.update(courseSchedule.signin(null));
            
            // 3. create course schedule attribute
            this.createCourseScheduleAttr(courseSchedule, siteServer);
            
            // 4. save or update coach student relation
            Principal coach = courseSchedule.getCoach();
            this.setupCoachStudentRelation(coach, courseSchedule);
            
            // 5. check course round finish status & do finish things
            CourseRound courseRound = this.checkCourseRoundStatus(coach, courseSchedule);
            
            // 6. update order status when course round finished
            this.checkOrderStatus(courseRound);
            
            // 7. save signin record
            this.createSigninRecord(courseSchedule);
            
            // 8. this.createCoachSigninRecord(courseSchedule);
            logger.info("current sign sever is [{}]", siteServer.getName());
            this.createCoachSigninRecord(courseSchedule, siteServer.getName());
            
            logger.info("update userSource");
            this.updateUserSource(courseSchedule);
            
            
            // 9. save bwhRecord
            if(isBwhNeeded) {
                if(bwhRecord.getBust() == null || bwhRecord.getWaist() == null || bwhRecord.getHips() == null) {
                    throw new BusinessException("三围数据不能为空");
                } else {
                    genericDaoSupport.save(bwhRecord.initilization(courseSchedule.getCourseRound(), siteServer));
                }
            }
            
            // 10. record first&last bwh&inbody
            if(!courseRound.getCourse().isExperience()) {
                List<CourseSchedule> courseSchedules = courseRoundDao.listCourseSchedule(courseRound, ScheduleStatus.FINISH);
                
                Date begin = courseSchedules.get(courseSchedules.size() - 1).getOccurDate();
                Date end = courseSchedules.get(0).getOccurDate();
                
                List<BwhRecord> bwhRecords = signinDao.listBwhRecord(courseRound.getStudent().getUser(), DateFormatUtils.format(begin, "yyyy-MM-dd") + " 00:00:00", DateFormatUtils.format(end, "yyyy-MM-dd") + " 23:59:59");
                
                List<InbodyRecord> inbodyRecords = signinDao.listInbodyRecord(courseRound.getStudent().getUser(), DateFormatUtils.format(begin, "yyyy-MM-dd") + " 00:00:00", DateFormatUtils.format(end, "yyyy-MM-dd") + " 23:59:59");
                
                if(!bwhRecords.isEmpty() || !inbodyRecords.isEmpty()) {
                    BwhRecord previousBwh = null;
                    BwhRecord laterBwh = null;
                    InbodyRecord previousInbody = null;
                    InbodyRecord laterInbody = null;
                    
                    if(!bwhRecords.isEmpty()) {
                        previousBwh = bwhRecords.get(bwhRecords.size() - 1);
                        laterBwh = bwhRecords.get(0);
                    }
                    
                    if(!inbodyRecords.isEmpty()) {
                        previousInbody = inbodyRecords.get(inbodyRecords.size() - 1);
                        laterInbody = inbodyRecords.get(0);
                    }
                    
                    CourseRoundResult result = courseRoundDao.loadCourseRoundResult(courseRound);
                    if(result == null) {
                        result = new CourseRoundResult(courseRound, previousBwh, laterBwh, previousInbody, laterInbody);
                        genericDaoSupport.save(result);
                    } else {
                        genericDaoSupport.update(result.edit(previousBwh, laterBwh).edit(previousInbody, laterInbody));
                    }
                }
            }
        }
    }
    
    /**
     * 
     * @param courseSchedule
     * @param siteServerManager
     */
    private void createCourseScheduleAttr(CourseSchedule courseSchedule, Principal siteServerManager) {
        
        // create signinPersonName attribute
        CourseScheduleAttribute signinPersonName = new CourseScheduleAttribute(courseSchedule, Constant.SIGNIN_PERSON_NAME, siteServerManager.getName());
        genericDaoSupport.save(signinPersonName);
        
        // create signinPersonId attribute
        CourseScheduleAttribute signPersonId = new CourseScheduleAttribute(courseSchedule, Constant.SIGN_PERSON_ID, siteServerManager.getIdString());
        genericDaoSupport.save(signPersonId);
        
        // create signinTime attribute
        CourseScheduleAttribute signinTime = new CourseScheduleAttribute(courseSchedule, Constant.SIGNIN_TIME, DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        genericDaoSupport.save(signinTime);
    }
    
    /**
     * 
     * @param coach
     * @param courseSchedule
     */
    private void setupCoachStudentRelation(Principal coach, CourseSchedule courseSchedule) {
        
        CoachStudentRelation coachStudentRelation = signinDao.loadCoachStudentRelation(coach, courseSchedule.getCourseType(), courseSchedule.getStudent());
        
        if(coachStudentRelation == null) {
            coachStudentRelation = new CoachStudentRelation(coach, courseSchedule);
            genericDaoSupport.save(coachStudentRelation);
        }else {
            genericDaoSupport.update(coachStudentRelation.updateTime());
        }
    }
    
    /**
     * 
     * @param coach
     * @param courseSchedule
     * @return
     */
    private CourseRound checkCourseRoundStatus(Principal coach, CourseSchedule courseSchedule) {
        
        // do sign in course round
        CourseRound courseRound = courseSchedule.getCourseRound();
        genericDaoSupport.update(courseRound.signin());
        
        if(courseRound.getCourseType().isIndividual() || courseRound.getCourseType().isSpecial() || courseRound.getCourseType().isPosture()) {
        	
            int courseAmount = courseRound.getCourseAmount();
            
            // count finished courseSchedule 
            int finishedSchedule = signinDao.countFinishedSchedule(courseRound, ScheduleStatus.FINISH);
            int allScheduleCount = signinDao.countAllSchedule(courseRound);
            
            // if course round finish count equal finished schedule and finished schedule is all schedule
            if(courseAmount == finishedSchedule && allScheduleCount == finishedSchedule) {
                genericDaoSupport.update(courseRound.finishCourseRound());
            }
        }
        
        return courseRound;
    
    }
    
   /**
    * 
    * @param courseRound
    */
    private void checkOrderStatus(CourseRound courseRound) {
        
        if(courseRound.isFinish()) {
            
            Order order = courseRound.getOrder();
            
            if(order != null) {
                order.orderStatus(OrderStatus.COMPLETE);
                genericDaoSupport.update(order);
            }
            
        }
    }
    
    /**
     * 
     * @param courseSchedule
     */
    private void createSigninRecord(CourseSchedule courseSchedule) {
        SigninRecord signinRecord = new SigninRecord(courseSchedule, true);
        genericDaoSupport.save(signinRecord);
    }
    
	private void updateUserSource(CourseSchedule courseSchedule) {
    	UserSource userSource = userSourceDao.load(courseSchedule.getStudentMobile());
    	if(userSource != null) {
    		if(userSource.getCoach() == null || userSource.getVisited() == null || userSource.getVisited() == false) {
    			CourseSchedule finishedFirst = signinDao.loadFirstFinish(courseSchedule.getStudent(), ScheduleStatus.FINISH);
    			userSource.setVisited(true);
    			userSource.setCoach(finishedFirst.getCoach());
    			userSource.setVisitedDate(finishedFirst.getOccurDate());
    		}
    	}
    }

    /**
     * 
     * @param courseSchedule
     */
    private void createCoachSigninRecord(CourseSchedule courseSchedule, String signPerson) {
        // save coach signin record first
        CoachSigninRecord signinRecord = new CoachSigninRecord(courseSchedule, signPerson);
        genericDaoSupport.save(signinRecord);
        
        // 用户已经下单了
        if(courseSchedule.getStudent().getUser().isOrdered()) {
        	
        	// 保存本月的签到统计记录
        	// save or update this month has coach signin statistic
            CoachSigninStatistic signinStatistic = signinDao.loadCoachSigninStatistic(courseSchedule.getCoach(),
                    DateFormatUtils.format(courseSchedule.getOccurDate(), "yyyy-MM"));
            if (signinStatistic == null) {
                signinStatistic = new CoachSigninStatistic(courseSchedule);
                signinStatistic.experienceHour(0);
                signinStatistic.giveHour(0);
                signinStatistic.hour(0);
                genericDaoSupport.save(signinStatistic);
            } else {
            	CourseUnit unit = courseSchedule.getCourse().getCourseUnit();
            	if(courseSchedule.getCourse().isExperience()) {
                	if(unit.equals(CourseUnit.NORMAL_EXPERIENCE)) {
                		genericDaoSupport.update(signinStatistic.experienceHour(1.5));
                	} else if(unit.equals(CourseUnit.NORMAL_WEEK_EXPERIENCE)) {
                		genericDaoSupport.update(signinStatistic.experienceHour(1));
                	} else if(unit.equals(CourseUnit.NORMAL_EXPERIENCE_WEEK) || 
                			unit.equals(CourseUnit.SPECIAL_EXPERIENCE_WEEK) || 
                			unit.equals(CourseUnit.SPECIAL_WEEK_EXPERIENCE) ||
                			unit.equals(CourseUnit.SPECIAL_EXPERIENCE)) {
                		genericDaoSupport.update(signinStatistic.hour(1).experienceHour(1));
                	}
                	
                } else if(courseSchedule.getCourse().isGive()) {
                	genericDaoSupport.update(signinStatistic.giveHour(1));
                } else {
                	if(courseSchedule.getCourseType().isPosture()) {
                		genericDaoSupport.update(signinStatistic.hour(2));
                	} else if(courseSchedule.getCourse().getCourseUnit().equals(CourseUnit.SPECIAL_UNION)) {
                    	genericDaoSupport.update(signinStatistic.hour(1.5));
                    } else {
                		genericDaoSupport.update(signinStatistic.hour(1));
                	}
                }
            }
        }
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.signin.service.SigninService#isBwhNeeded(co.fitstart.entity.course.round.CourseRound)
     */
    public boolean isBwhNeeded(CourseRound courseRound) {
        if(!courseRound.getCourse().isExperience() && courseRound.getCourseStatus().isReady() && (courseRound.getFullInTarget() == null || courseRound.getFullInTarget())) {
            BwhRecord bwhRecord = bwhRecordDao.loadLatest(courseRound);
            
            if(bwhRecord == null || DateUtils.isSameDay(bwhRecord.getCreateTime(), DateUtils.addWeeks(new Date(), -2)) || bwhRecord.getCreateTime().before(DateUtils.addWeeks(new Date(), -2))) {
                return true;
            }
        }
        
        return false;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.signin.service.SigninService#isBwhNeeded(co.fitstart.entity.course.schedule.CourseSchedule)
     */
    public boolean isBwhNeeded(CourseSchedule courseSchedule) {
    	if(courseSchedule.getCourseType().isCard()) {
    		return false;
    	} else {
    		int count = courseRoundDao.count(courseSchedule.getCourseRound(), ScheduleStatus.FINISH);
        	return count == 0 || count %12 ==11;
    	}
    }
}
